

import UIKit
import AFNetworking

//错误的类别标记
private let BBBErrorDomainName = "com.baidu.error.network"

//网络访问错误信息处理 -- 枚举,是定义一组类似的值
//swift中的枚举可以定义函数和属性,和类有点像
///定义一个枚举,用来返回错误回调
private enum BBBNetworkError:Int{

    case emptyDataError = -1
    case emptyTokenError = -2
    
    //错误描述
    private var errorDescrption:String{
        
        switch self{
            
        case .emptyDataError: return "空数据"
        case .emptyTokenError: return "Token为空"
        }
    }
    
    //根据枚举类型,返回对应的错误
    private func error() -> NSError{
        
        return NSError(domain: BBBErrorDomainName, code: rawValue, userInfo: [BBBErrorDomainName:errorDescrption])
    }
}

///网络访问方法枚举
//private enum BBBNetworkmethod: String{
//    
//    case GET:"GET"
//    case POST:"POST"
//}
private enum BBBNetworkMethod: String {
    case GET = "GET"
    case POST = "POST"
}


//  使用单例是为了防止过于依赖第三方框架,降低耦合度
//  提供一个单例,用来访问新浪微博的api
class NetworkTools: AFHTTPSessionManager
{
    //App Key
    private let clientId = "2341865434"
    //App Secret
    private let appSecret = "771cf5ec7ff3df293ca070283902da54"
    //回调地址
    let redirectUri = "http://www.baidu.com"
    
    static let sharedTools:NetworkTools = {
        
        let baseUrl = NSURL(string: "https://api.weibo.com")!
        
        let tools = NetworkTools(baseURL: baseUrl)
        
        tools.responseSerializer.acceptableContentTypes = NSSet(objects: "application/json", "text/json", "text/javascript","text/plain") as Set<NSObject>
        
        return tools
        
    }()
    
    //MARK: - 加载用户数据        --> 职责,网络访问,获取到dict
    /*
        uid 用户代号字符串
        finished: 完成回调
    */
    func loadUserInfo(uid:String,finished:BBBNetFinishedCallBack){
        
        //guard不仅可以用let 还可以用var
        guard var params = tokenDict(finished) else{
        
            //params在此为nil,一般直接返回
            return
        }
        
        let urlString = "2/users/show.json"
        params["uid"] = uid
        
        //发送网络请求
        request(BBBNetworkMethod.GET, urlString: urlString, params: params, finished: finished)
    }
    
    
    ///MARK: - 网络1.OAuth授权信息:
    //返回OAuth授权地址
    func oauthUrl()-> NSURL{
    
        //拼接地址
        let urlString = "https://api.weibo.com/oauth2/authorize?client_id=\(clientId)&redirect_uri=\(redirectUri)"
     
        return NSURL(string: urlString)!
    }
    
    
    ///MARK: - 网络3.加载TOKEN
                                    //这里的result是最后的结果,但是他在单例中,需要传入到视图中
    func loadAccessToken(code:String,finished:BBBNetFinishedCallBack){
    
        //这是TOKEN的那个URL
        let urlString = "https://api.weibo.com/oauth2/access_token"
        
        //将网页中的5个参数给服务器发送过去,服务器返回给TOKEN
        let params = ["client_id":clientId,
            "client_secret":appSecret,
            "grant_type":"authorization_code",
            "code":code,
            "redirect_uri":redirectUri
        ]
        
        //发送POST请求
        request(BBBNetworkMethod.POST, urlString: urlString, params: params, finished: finished)
        
    }
    
    //MARK: - 封装AFN网络方法,便于替换网络访问方法,第三方框架的网络代码全部集中在此
    typealias BBBNetFinishedCallBack = (result:[String:AnyObject]?,error:NSError?)->()
    //封装AFN网络方法,第三方框架代码全部集中在这里
    private func request(method:BBBNetworkMethod,urlString:String,params:[String:AnyObject],finished:BBBNetFinishedCallBack){
    
        //1.定义成功闭包
        let successCallBack: (NSURLSessionDataTask!,AnyObject!) -> Void = {(_,JSON) in
        
        
            if let result = JSON as? [String:AnyObject]{
                
                //有结果的回调
                finished(result: result, error: nil)
            }
            else
            {
                //没有错误,同时没有结果
                print("没有数据 GET Request \(urlString)")
                finished(result: nil, error: BBBNetworkError.emptyDataError.error())
            }
        }
        
        //2.定义失败闭包
        let failedCallBack:(NSURLSessionDataTask!,NSError!) -> Void = {(_,error) in
        
            print(error)
            
            finished(result: nil, error: error)
        }
        
        //3.根据method来选择执行的方法
        switch method{
        
        case .GET:
            GET(urlString,parameters:params,success:successCallBack,failure:failedCallBack)
            
        case .POST:
            POST(urlString,parameters:params,success:successCallBack,failure:failedCallBack)
        }
    }
    
    //MARK: - 发布微博方法
    func sendStatus(status:String,image:UIImage?,finished:BBBNetFinishedCallBack){
    
        guard var params = tokenDict(finished) else{
            
            return
        }
        params["status"] = status
        
        if image == nil{
        
            //用封装好的发送网络请求的方法进行发送微博
            request(BBBNetworkMethod.POST, urlString: "2/statuses/update.json", params: params, finished: finished)
        }else{
            
            //在使用AFN的时候,如果与BaseURL不一样,可以直接使用全路径
            uploadImage("https://upload.api.weibo.com/2/statuses/upload.json", image: image!, params: params, finished: finished)
        }
        
    }
    
    //MARK: - 加载微博数据
    /*
        拓展两个参数
        since_id 返回ID比since_id大的微博(即比since_id时间晚的微博),默认为0
        max_id   返回ID小于或等于max_id的微博,默认为0
    */
    func loadStatus(since_id:Int,max_id:Int,finished:BBBNetFinishedCallBack){
    
        //判断token是否存在,为nil直接返回
        //guard是swift2.0后的新语法,和if let刚好相反
        //let获得变量,后续可以直接使用,而且一定不为nil
        guard var params = tokenDict(finished) else{
        
            //params在此为nil,一般直接返回不做操作
            return
        }
        
        if since_id > 0{
            
            params["since_id"] = since_id
        }
        
        if max_id > 0{         // max_id = ((self.statuses?.last?.id)! - 1) ?? 0
            
            params["max_id"] = max_id - 1
        }
        
        //代码如果运行至此,params一定有值
        let usrString = "2/statuses/home_timeline.json"
        
        request(BBBNetworkMethod.GET, urlString: usrString, params: params, finished: finished)
    }
    
    //加载用户信息 和 加载微博数据都需要判断token,所以可以封装
    //以下就是封装
    private func tokenDict(finished:BBBNetFinishedCallBack) -> [String:AnyObject]?{
    
        //判断token是否存在
        if UserAccount.sharedUserAccount?.access_token == nil{
            
            //错误回调
            let error = BBBNetworkError.emptyTokenError.error()
            
            print(error)
            
            finished(result: nil, error: error)
            
            return nil
        }
        
        //生成token字典返回
        return ["access_token":UserAccount.sharedUserAccount!.access_token!]
    }
    
    //发送图片
    private func uploadImage(urlString:String,image:UIImage,params:[String:AnyObject],finished:BBBNetFinishedCallBack){
        
        //1.定义成功闭包
        let successCallBack: (NSURLSessionDataTask!,AnyObject!) -> Void = {(_,JSON) in
            
            
            if let result = JSON as? [String:AnyObject]{
                
                //有结果的回调
                finished(result: result, error: nil)
            }
            else
            {
                //没有错误,同时没有结果
                print("没有数据 GET Request \(urlString)")
                finished(result: nil, error: BBBNetworkError.emptyDataError.error())
            }
        }
        
        //2.定义失败闭包
        let failedCallBack:(NSURLSessionDataTask!,NSError!) -> Void = {(_,error) in
            
            print(error)
            
            finished(result: nil, error: error)
        }
        
        POST(urlString, parameters: params, constructingBodyWithBlock: { (formData) -> Void in
        
            //1. 将image转换成二进制数据
            let data = UIImagePNGRepresentation(image)!
            
            //2. 构造数据体
            /*
                name:服务器要求的字段名
                fileName:保存在服务器的文件名,如果接口文档没有指定文件名参数,可以随便写
                mimeTye:告诉服务器上传文件的类型,如果不想告诉服务器,可以使用application/octet-stream
                格式: 大类/小类
                常见格式:
                    image/png
                    image/jpg
                    image/gif
                    image/html
                    image/plain
            */
            formData.appendPartWithFileData(data, name:"pic", fileName: "xxoo", mimeType: "application/octet-stream")
            
            }, success: successCallBack, failure: failedCallBack)
    }
}





































